import RibbonsOption from './RibbonsOption';
import Point from './Point';

var _default_callback = function(status) {
  console.log(status);
}

export default class Ribbons {
  /** @type {HTMLCanvasElement} */
  _canvas = null;
  _context = null;
  _sto = null;
  _width = 0;
  _height = 0;
  /** @type {Ribbon[]} */
  _ribbons = [];
  _options = new RibbonsOption();
  
  _playing = false;
  _pause = false;
  _stop = true;
  /** 当状态发生变化的通知方 */
  _listener = _default_callback;
  
  /** @type {Element} */
  parent = null;

  constructor(options, parent) {
    this.setOptions(options);
    this.parent = parent;
    
    this.init();
  }

  setOptions(options) {
    this._options.set(options);
  }

  init() {
    console.log(this._options);

    try {
      this._canvas = document.createElement("canvas");
      this._canvas.style["display"] = "block";
      this._canvas.style["position"] = "fixed";
      this._canvas.style["margin"] = "0";
      this._canvas.style["padding"] = "0";
      this._canvas.style["border"] = "0";
      this._canvas.style["outline"] = "0";
      this._canvas.style["left"] = "0";
      this._canvas.style["top"] = "0";
      this._canvas.style["z-index"] = "-1";
      this._canvas.id = "bgCanvas";
      this._onResize();

      this._context = this._canvas.getContext("2d");
      this._context.clearRect(0, 0, this._width, this._height);
      this._context.globalAlpha = this._options.colorAlpha;

      window.addEventListener("resize", this._onResize.bind(this));
      // window.addEventListener("scroll", this._onScroll);

      if (this.parent) {
        this.parent.appendChild(this._canvas);
      } else {
        window.addEventListener("resize", this._onResize.bind(this));
        // window.addEventListener("scroll", this._onScroll);
        // 如果不传 parent, 则当网页背景处理
        document.body.appendChild(this._canvas);
      }

    } catch (e) {
      console.warn("Canvas Context Error: " + e.toString());
      return;
    }
  }

  /**
   * Create a new random ribbon and to the list
   * 创建一个随机飘带, 并将它加入到列表中
   */
  addRibbon() {
    // movement data
    var dir = Math.round(random(1, 9)) > 5 ? "right" : "left",
      stop = 1000,
      hide = 200,
      min = 0 - hide,
      max = this._width + hide,
      movex = 0,
      movey = 0,
      startx = dir === "right" ? min : max,
      starty = Math.round(random(0, this._height));

    // asjust starty based on options
    if (/^(top|min)$/i.test(this._options.verticalPosition)) {
      starty = 0 + hide;
    } else if (/^(middle|center)$/i.test(this._options.verticalPosition)) {
      starty = this._height / 2;
    } else if (/^(bottom|max)$/i.test(this._options.verticalPosition)) {
      starty = this._height - hide;
    }

    // ribbon sections data
    var ribbon = new Ribbon(this),
    point1 = new Point(startx, starty),
    point2 = new Point(startx, starty),
    point3 = null,
    color = Math.round(random(0, 360)),
    delay = 0;

    // buils ribbon sections
    while (true) {
      if (stop <= 0) break;stop--;

      movex = Math.round((Math.random() * 1 - 0.2) * this._options.horizontalSpeed);
      movey = Math.round((Math.random() * 1 - 0.5) * (this._height * 0.25));

      point3 = new Point();
      point3.copy(point2);

      if (dir === "right") {
        point3.add(movex, movey);
        if (point2.x >= max) break;
      } else if (dir === "left") {
        point3.subtract(movex, movey);
        if (point2.x <= min) break;
      }
      // point3.clampY( 0, this._height );

      ribbon.sections.push(new Section(point1.x, point1.y, point2.x, point2.y, point3, color, delay, dir));

      point1.copy(point2);
      point2.copy(point3);

      delay += 4;
      color += this._options.colorCycleSpeed;
    }
    this._ribbons.push(ribbon);
  }

  /**
   * 不停地绘制, 每次浏览器刷新都绘制一下
   */
  play() {
    this._pause = this._stop = false;
    this._playing = true;
    this._listener(this.status());
    this.render();
  }

  pause() {
    this._pause = true;
  }

  stop() {
    this._stop = true;
    if (this._pause) {
      // 强制调用一次, 清屏 + 发送状态
      this.render();
    }
  }

  status() {
    if (this._playing) {
      return "playing";
    }
    if (this._pause) {
      return "paused";
    }
    if (this._stop) {
      return "stoped";
    }
    return "unknowed";
  }

  /**
   * @param {function | null} l 状态改变时的 callback. 输入状态 string
   */
  setListener(l) {
    if (typeof(l) === 'function') {
      this._listener = l;
    } else {
      this._listener = _default_callback;
    }
  }

  /**
   * 绘制 ribbons
   */
  render() {
    // 清空 Canvans
    this._context.clearRect(0, 0, this._width, this._height);

    if (this._stop) {
      this._ribbons = [];
      this._playing = false;
      this._listener(this.status());
      return;
    }

    // 清理掉那些动画播放完毕的 Ribbon 
    for (let i = 0, t = this._ribbons.length; i < t; ++i) {
      if (!this._ribbons[i] || this._ribbons[i].finished) {
        this._ribbons.splice(i, 1);
      }
    }

    for (let a = 0; a < this._ribbons.length; ++a) {
      this._ribbons[a].draw();
    }

    // Canvas 中如果飘带数不够, 需要添加
    if (this._ribbons.length < this._options.ribbonCount) {
      this.addRibbon();
    }

    if (!this._pause) {
      requestAnimationFrame(this.render.bind(this));
    } else {
      this._playing = false;
      this._listener(this.status());
    }
  }

  _onResize() {
    let el = (this.parent) ? this.parent : document.body;
    this._width = el.clientWidth;
    this._height = el.clientHeight;

    if (this._canvas) {
      this._canvas.width = this._width;
      this._canvas.height = this._height;

      if (this._context) {
        this._context.globalAlpha = this._options.colorAlpha;
      }
    }
  }
}

/**
 * 一个飘带的构成元素是很多三角形片段
 */
class Ribbon {
  /** @type {Ribbons} */
  parent = null;
  /** 三角形片段的个数: 我这个飘带最多画多少三角形片段. 画完那么多三角形, 然后这些三角形完成自己的动作后, 飘带就寿终正寝了 */
  length = 0; // final

  /** @type {Section[]} */
  sections = [];

  finished = false;

  option = new RibbonsOption();

  /**
   * @param {Ribbons} ribbons 
   */
  constructor(ribbons) {
    this.parent = ribbons;
    this.option.set(ribbons._options);
  }

  draw() {
    let numSections = this.sections.length,
      numDone = 0;

    for (let i = 0; i < numSections; ++i) {
      if (this.drawSection(i)) {
        numDone++; // section done
      }
    }
    if (numDone >= numSections) {
      this.finished = true;
    }
  }

  /**
   * @param {number} this.sections 的索引
   * @returns 是否整个飘带绘制成功 (不是单个 section). 返回 true 就说明后面的 section 不用画了
   */
  drawSection(i) { // _drawRibbonSection
    let section = this.sections[i];

    if (!section) {
      return false;
    }

    return section.draw(this.option, this.parent._context);
  }
}

class Section {
  /** @type {Point} */
  point1;
  /** @type {Point} */
  point2;
  /** @type {Point} */
  point3;
  /** @type {number} */
  color;
  /** @type {number} */
  delay;
  /** @type {string} */
  dir;
  alpha = 0;
  phase = 0;

  /**
   * @param {number} p1x 
   * @param {number} p1y 
   * @param {number} p2x 
   * @param {number} p2y 
   * @param {Point} p3 
   * @param {number} color 
   * @param {number} delay 
   * @param {string} dir 方向: "left" | "right"
   */
  constructor(p1x, p1y, p2x, p2y, p3, color, delay, dir) {
    this.point1 = new Point(p1x, p1y);
    this.point2 = new Point(p2x, p2y);
    this.point3 = p3;
    this.color = color;
    this.delay = delay;
    this.dir = dir;
  }

  /**
   * @param {RibbonsOption} option 
   * @param {*} painter 就是 canvas 的 context
   */
  draw(option, painter) {
    if (this.phase >= 1 && this.alpha <= 0) {
      return true; // done
    }

    if (this.delay <= 0) {
      this.phase += 0.02;
      this.alpha = Math.sin(this.phase);
      this.alpha = this.alpha <= 0 ? 0 : this.alpha;
      this.alpha = this.alpha >= 1 ? 1 : this.alpha;

      if (option.animateSections) {
        let mod = Math.sin(1 + this.phase * Math.PI / 2) * 0.1;

        if (this.dir === "right") {
          this.point1.add(mod, 0);
          this.point2.add(mod, 0);
          this.point3.add(mod, 0);
        } else {
          this.point1.subtract(mod, 0);
          this.point2.subtract(mod, 0);
          this.point3.subtract(mod, 0);
        }
        this.point1.add(0, mod);
        this.point2.add(0, mod);
        this.point3.add(0, mod);
      }
    } else {
      this.delay -= 0.5;
    }

    let s = option.colorSaturation,
      l = option.colorBrightness,
      c = "hsla(" + this.color + ", " + s + ", " + l + ", " + this.alpha + " )";

    painter.save();
    painter.beginPath();
    painter.moveTo(this.point1.x, this.point1.y);
    painter.lineTo(this.point2.x, this.point2.y);
    painter.lineTo(this.point3.x, this.point3.y);
    painter.fillStyle = c;
    painter.fill();

    if (option.strokeSize > 0) {
      painter.lineWidth = option.strokeSize;
      painter.strokeStyle = c;
      painter.lineCap = "round";
      painter.stroke();
    }
    painter.restore();

    return false; // not done yet
  }
}

/**
 * @param {number} min 
 * @param {number} max 
 */
var random = function (min, max) {
  return Math.random() * (max - min) + min;
};
